
#include <BFile>
#include <BXmlDocument>
#include "member_BXmlDocument.h"
#include "member_BXmlNode.h"

using namespace BWE;

#define member					(*(member_BXmlDocument*)_ptr)
#define member_allocate()		_ptr = new member_BXmlDocument(this)
#define member_release()		delete (member_BXmlDocument*)_ptr

BXmlDocument::BXmlDocument()
{
	member_allocate();
}
BXmlDocument::~BXmlDocument()
{
	member_release();
}

void BXmlDocument::setVersion(const BString& version)
{
	if (member.version != version)
	{
		member.version = version;
	}
}
const BString& BXmlDocument::version() const
{
	return member.version;
}

void BXmlDocument::setEncoding(const BString& encoding)
{
	if (member.encoding != encoding)
	{
		member.encoding = encoding;
	}
}
const BString& BXmlDocument::encoding() const
{
	return member.encoding;
}

bool BXmlDocument::setFormat(const BString& format)
{
	if (member.format.text != format)
	{
		member.format.fromString(format);
		return true;
	}
	return true;
}
const BString& BXmlDocument::format() const
{
	return member.format.text;
}

BXmlNode* BXmlDocument::load(const BString& fileName)
{
	BString xmltext;
	if (BFile::Load(fileName, xmltext))
		return read(xmltext);
	return 0;
}
bool BXmlDocument::save(const BString& fileName, const BXmlNode* root) const
{
	BString xmltext;
	if (write(xmltext, root))
		return BFile::Save(fileName, xmltext);
	return false;
}

BXmlNode* BXmlDocument::read(const BStream& stream)
{
	BString xmltext;
	char code = 0;
	while (stream.read(code))
	{
		xmltext.append(code);
	}
	return read(xmltext);
}
bool BXmlDocument::write(BStream& stream, const BXmlNode* root) const
{
	BString xmltext;
	write(xmltext, root);
	return stream.write(xmltext.cstr(), xmltext.size());
}

BXmlNode* BXmlDocument::read(const BString& xmltext)
{
	BString text;
	char linebreak = 0;
	char code = 0;
	int pos = 0;
	while (pos < xmltext.size())
	{
		code = xmltext[pos++];
		if (code == '\n')
			linebreak = true;
		if (text.empty() && code != '<')
			continue;
		text.append(code);
		if (code == '>')
		{
			if (text.beginWith("<!"))
			{
				text.reset();
				continue;
			}
			if (text.beginWith("<?xml"))
			{
				if (text.endWith("?>"))
				{
					text.remove(0, 5);
					text.remove(text.size() - 2, 2);
					BXmlNode xml;
					member.readAttribs(&xml, text);
					const BString& version = xml.attrib("version");
					const BString& encoding = xml.attrib("encoding");
					member.version = version;
					member.encoding = encoding;
				}
				text.reset();
				linebreak = 0;
				continue;
			}
			break;
		}
	}
	if (text.first() != '<' || text.last() != '>')
		return 0;

	BString name;
	int name_end = member.readTagName(text, name);
	member.root = new BXmlNode(name);
	xml_member(member.root)->format.head_prev = linebreak;
	linebreak = 0;

	if (text.endWith("/>"))
	{
		text.remove(0, name_end);
		text.remove(text.size() - 2);
		member.readAttribs(member.root, text);
	}
	else
	{
		text.remove(0, name_end);
		text.remove(text.size() - 1);
		member.readAttribs(member.root, text);
		member.readNode(member.root, xmltext, pos);
	}

	member.makeFormat(member.root);
	return member.root;
}
bool BXmlDocument::write(BString& xmltext, const BXmlNode* root) const
{
	if (member.version.size() || member.encoding.size())
	{
		xmltext << "<?xml";
		if (member.version.size())
			xmltext << " version=\"" << member.version << '\"';
		if (member.encoding.size())
			xmltext << " encoding=\"" << member.encoding << '\"';
		xmltext << "?>\n";
	}
	member.writeNode(root, xmltext, 0);
	return true;
}

void BXmlDocument::setRoot(BXmlNode* root)
{
	member.root = root;
}
BXmlNode* BXmlDocument::root()
{
	return member.root;
}
